#include <mips/regdef.h>
#include <sys/syscall.h>
#define SSIZE		(80)
#define	O_RA		(76)
#define	O_FP		(72)
#define	O_GP		(68)
#define O_ARG0		0
#define O_ARG1		4
#define O_ARG2		8
#define BUFFER		16
#define VECTOR		20
#define CONTADOR	24
#define LONGITUD_DELIMITADOR	40
#define octal	28
#define sl	32
#define bitsSobrantes	36
#define EJECUCION_CORRECTA                              0
#define ERROR_LECTURA                                   1
#define ERROR_ESCRITURA                                 2
#define ERROR_DELIMITADORES                             3
#define ERROR_ARGUMENTOS_INCORRECTOS    4

#define sr 		5

       .data
		lectura:
			.space	1
		escritura:
			.space	1

		##FIN - Vector "const char *odump_errmsg[]" que contiene los mensajes de error


//int odump encode(int infd, int outfd, struct odump opts *opts)


			.text
			.align 	2
			.globl	odump_encode
			.ent	odump_encode

odump_encode:
			.frame 	$fp,SSIZE,ra
   			.set  	noreorder
   			.cpload t9
   			.set  	reorder

   			## creo stack frame
   			subu	sp,sp,SSIZE
			sw		gp,O_GP(sp)
			sw		$fp,O_FP(sp)
			sw		ra,O_RA(sp)
			move	$fp,sp
			sw		a0,O_ARG0($fp)
			sw		a1,O_ARG1($fp)
			sw		a2,O_ARG2($fp)

			## calculo longitud del delimitador
			lw		t0,O_ARG2($fp)
			lw		a0,4(t0)
			la		t9,mystrlen
			jal		ra,t9
			sw		v0,LONGITUD_DELIMITADOR($fp)

			## inicializo contador para longitud por linea
			sw		zero,CONTADOR($fp)

			//inicializo para moverme en los bits
			sw		zero,sl($fp)

leer1byte:
			li		v0,SYS_read
			lw		a0,O_ARG0($fp)		## a0 = infd
			la		a1,BUFFER($fp)		## a1 = direccion de BUFFER
			li		a2,1				## a2 = 1 byte
			syscall

			## inicializo los bits Sobrantes
			lw		t6,bitsSobrantes($fp)	// t6 = bitsSobrantes
			li		t6,8
			sw		t6,bitsSobrantes($fp)	//bitsSobrantes = t6

			## verifico EOF y errores
			beq		v0,zero,fin_odump_encode	// si v0=0 es EOF
		 	bne		a3,zero,error_lectura_encode	// si a3!=0 hay un error
		 	bne		v0,1,error_lectura_encode		// si no leyo 1 byte hay un error


primeraLectura:
			## aumento contador
			lw		t6,CONTADOR($fp)	// t6 = CONTADOR
			addiu	t6,t6,1				// t6 = CONTADOR + 1
			sw		t6,CONTADOR($fp)	//CONTADOR = t6

			li 		t0,48 			// t0 = CERO
			la 		t1,BUFFER($fp)		// t1 = direccion del BUFFER
			lb 		t1,0(t1) 			// t1 = *BUFFER
			li 		t2,0 				// t2 contador
			la		t3,VECTOR($fp)		// t3 = direccion del VECTOR
			//addiu	t3,t3,2				// aumento 2 posiciones para grabar de atras para adelante

			srl 	t3,t1,6 				//me quedo con los dos primeros bits
			addi 	t3,t3,48 				//le sumo el ascii del 0
			//sb 		t4,0(t3)
			sw		t3,octal($fp)

			//voy guardando los shifts que realizo
			li		t0,2
			sw		t0,sl($fp)

			## resto los bits procesados
			lw		t6,bitsSobrantes($fp)	// t6 = bitsSobrantes
			add		t6,t6,-2				// t6 = bitsSobrantes - 2
			sw		t6,bitsSobrantes($fp)	//bitsSobrantes = t6

			b		escribirOctal

mientras:
			lw 		t1,BUFFER($fp)		// t1 = direccion del BUFFER
			lw		t5,sl($fp)

			## resto los bits procesados
			lw		t6,bitsSobrantes($fp)	// t6 = bitsSobrantes
			add		t6,t6,-3				// t6 = bitsSobrantes - 3
			sw		t6,bitsSobrantes($fp)	//bitsSobrantes = t6

			//me quedo con los 3 bits del medio o los ultimos 3, dependiendo del valor de t5
			sll 	t3,t1,t5
			srl		t3,t3,sr
			addiu 	t3,t3,48 			//le sumo el ascii del 0
			sw		t3,octal($fp)
			//sb 		t4,0(t3)
			addiu	t5,t5,3
			sw		t5,sl($fp)
			b		escribirOctal		//lo escribo en el archivo

escribirOctal:
			## escribe el octal
			li		v0,SYS_write
			lw		a0,O_ARG1($fp)		// a0 = outfd
			la		a1,octal($fp)		// a1 = direccion de VECTOR
			li		a2,1				// a2 = 1 byte
			syscall

			## verifico de errores de escritura
			bne		v0,1,error_escritura_encode		// si no escribio 1 byte hay un error

			//mientras bitsSobrantes <> 0 -> proceso el resto de los bits
			lw		t6,bitsSobrantes($fp)	// t6 = bitsSobrantes
			bne		t6,zero, mientras

			//si los bits sobrantes valen 0 escribo delimitador (esto se verifica dentro de SaltoDeLinea)
			beq		t6,zero,saltoDeLinea

escribirDelimitador:
			## escribe el delimitador
			li		v0,SYS_write
			lw		a0,O_ARG1($fp)		// a0 = outfd
			lw		t0,O_ARG2($fp)		// t0 = odump_pts
			lw		a1,4(t0)			// a1 = direccion de delimitador
			lw		a2,LONGITUD_DELIMITADOR($fp)	// a2 = longitud delimitador
			syscall

			## verifico de errores de escritura
			lw		t0,LONGITUD_DELIMITADOR($fp)	// t0 = longitud delimitador
		 	bne		v0,t0,error_escritura_encode	// si no escribio la cantidad de bytes del delimitador hay error
		 	b		leer1byte	//## lenght!=CONTADOR -> proximoByte

saltoDeLinea:
			## calculo si hay que hacer salto de linea
			lw		t0,O_ARG2($fp)		// t0 = odump_pts
			lw		t0,0(t0)			//## t0 = length
			lw		t1,CONTADOR($fp)	//## t1 = CONTADOR
			bne		t0,t1,escribirDelimitador	//## lenght!=CONTADOR -> proximoByte
			beq		t0,zero,leer1byte	//## length=0 -> proximoByte



			## si llego aca es porque hay que escribir el salto de linea
			li		v0,SYS_write
			lw		a0,O_ARG1($fp)		## a0 = outfd
			la		a1,newLine			## a1 = direccion de newLine
			li		a2,1				## a2 = 1 byte
			syscall

			## verifico de errores de escritura
			bne		v0,1,error_escritura_encode		## si no escribio 1 byte hay un error

			## vuelvo a inicializar CONTADOR
			sw		zero,CONTADOR($fp)

proximoByte:

			## si es stdin y se leyo ya el salto de linea es fin
			beq  s2,1,fin_odump_encode

			## vuelvo a leer el siguiente byte
			b leer1byte

error_lectura_encode:
			li		v0,ERROR_LECTURA
			b		fin_odump_encode

error_escritura_encode:
			li		v0,ERROR_ESCRITURA
			b		fin_odump_encode

ejecucion_correcta_encode:
			li		v0,EJECUCION_CORRECTA

fin_odump_encode:
			## destruyo stack frame
			move	sp,$fp
			lw		gp,O_GP(sp)
			lw		$fp,O_FP(sp)
			lw		ra,O_RA(sp)
			addiu	sp,sp,SSIZE
			jr		ra
			.end	odump_encode

			.data
newLine:
			.ascii 	"\n"
error_encode:
			.ascii	"Se ha producido un error\n"

########################################################################################################################################################

		####		STACK FRAME DECODE
		####	tengo 3 parametros punteros,
		####	6 variables locales y
		####	es no leaf con 2 parametros (llama a convertir)
		####
		####	  68|--------------|
		####		|     opt	   |
		####	  64|--------------|
		####		|	   fout    |
		####	  60|--------------|
		####		|      f       |
		####	  56|--------------|
		####		|\\\\\\\\\\\\\\|
		####	  52|--------------|
		####		|      ra      |
		####	  48|--------------|	R
		####		|	   fp      |	A
		####	  44|--------------|
		####		|      gp      |
		####	  40|--------------|##---
		####	  	|    int k     |
		####	  36|--------------|
		####	  	|     int j    |	L
		####	  32|--------------|	T
		####	  	|     int mid  |	A
		####	  28|--------------|
		####	  	|     int conv |
		####	  24|--------------|
		####	  	| 	  int i    |
		####	  20|--------------|
		####	  	|     int bin  |
		####	  16|--------------|##---
		####		|\\\\\\\\\\\\\\|
		####	  12|--------------|	A
		####		| int delim	   |    B
		####	   8|--------------|	A
		####		|    int i     |
		####	   4|--------------|
		####		||char bin|\\\\|
		####	   0|--------------|##--

	.text                   	## codigo en sector del texto
	.align 2
	.ent 	odump_decode        ## declaro el nombre de la funcion
	.globl 	odump_decode       	## declaro que sera global

odump_decode:
	.frame 	$fp,56,ra			## defino mi frame
	.set 	noreorder
	.cpload t9
	.set 	reorder

	subu 	sp,sp,56			## sp apunta al final

	.cprestore 40				## salvo a gp
	sw 		$fp,44(sp)			## salvo a fp
	sw 		ra,48(sp)			## como es caller, salvo a ra

	move 	$fp,sp				## fp apunta donde apunta sp

	#### Guardo los argumentos que le pasan a la funcion

	sw		a0,56($fp)			## recibo el file descriptor del input
	sw		a1,60($fp)			## recibo el file descriptor del output
	sw		a2,64($fp)			## recibo el puntero al opt

	move	s0,a0
	move	s1,a1
	move	s2,a2

	lw		s3,4(s2)			## busco el delim del opt

	#### defino e inicializo variables locales

	sw 		zero,24($fp)		## conv=0
	sw 		zero,28($fp)		## mid=0

	#### comienza codigo

	move 	a0,s3					## leo delim del struct
	jal 	mystrlen

	move 	t0, v0
	sw 		t0, 32($fp)			##j=strlen(delim)

	sb		zero,lectura
	move	a0, s0				## a0=file descriptor
	la		a1, lectura			## a1=bin
	li		a2, 1				## a2=1
	jal		my_read				## bin=fgetc(f)

	beqz	v0,errorLectura

	lb		t0,lectura
	sw		t0,16($fp)

while:
	lw		t0,16($fp)
	beqz 	t0, fin_while   	##si feof(f) es 0 termina el while

	li 		t5, 2
	sw		t5, 20($fp)			##i=2

for:
	lw 		t5, 20($fp)
	bltz 	t5, fin_for			##si i<0 me voy del for

	lbu   	a0, 16($fp)			##a0=bin
	lw   	a1, 20($fp)			##a1=i
	jal 	convertir			## preparo parametros y llamo a convertir

	sw 		v0,24($fp)			##conv=convertir(bin, i)
	lw		t0, 24($fp)

	li 		t1, -1
	beq 	t0, t1, fin_error	## if conv==-1 error

	lw 		t1, 28($fp)			##mid
	add 	t1, t1, t0
	sw 		t1, 28($fp)			##mid=mid+conv

	sb		zero,lectura
	move	a0, s0				## a0=file descriptor
	la		a1, lectura			## a1=bin
	li		a2, 1				## a2=1
	jal		my_read				## bin=fgetc(f)

	beqz	v0,errorLectura

	lb		t0,lectura
	sw		t0,16($fp)

	lw 		t5, 20($fp)
	sub 	t5, t5, 1
	sw 		t5, 20($fp)			##i--

	j 		for

fin_for:

	sb		zero,escritura

	la		t0,28($fp)
	lw		t0,0(t0)
	sb		t0,escritura

	move	a0, s1				## a0=file descriptor
	la		a1,escritura			## a1=datos
	li		a2, 1				## a2=tamanio

	jal		my_write

	sw 		zero, 28($fp)			##mid=0

	li		t1, 10					##Cargo ASCII '\n'
	lw  	t6, 16($fp)				##if bin=='\n'
	bne 	t6, t1, salto_delimitador

	sb		zero,lectura
	move	a0, s0				## a0=file descriptor
	la		a1, lectura			## a1=bin
	li		a2, 1				## a2=1
	jal		my_read				## bin=fgetc(f)

	beqz	v0,errorLectura

	lb		t0,lectura
	sw		t0,16($fp)

	j 		while

salto_delimitador:

		lw 		t0, 32($fp)			##j
		sw 		t0, 36($fp)			##k=j

loop_delimitador:

		lw 		t1, 36($fp)
		li 		t3,1
		blt 	t1, t3, while		##if k<1

		sb		zero,lectura
		move	a0, s0				## a0=file descriptor
		la		a1, lectura			## a1=bin
		li		a2, 1				## a2=1
		jal		my_read				## bin=fgetc(f)

		beqz	v0,errorLectura

		lb		t0,lectura
		sw		t0,16($fp)

		lw 		t1, 36($fp)
		sub 	t1, t1, 1
		sw 		t1, 36($fp)			##k--

		j 		loop_delimitador

fin_error:
		li 		v0, 3				##devuelvo 3
		j 		fin_while
errorLectura:
		li		v0,1				##devuelvo 1

fin_while:
		move	sp, $fp
		lw		gp, 40(sp)			## restauro gp
		lw		$fp, 44(sp)			## restauro fp
		lw		ra, 48(sp)			## restauro ra
		addu	sp, sp, 56			## restauro sp

		j		ra					## retorno hacia la caller

   		.end odump_decode         	## fin odump_decode


	.globl my_read
	.ent my_read

my_read:
	   	.frame $fp,32, ra
	   	.set  noreorder
	   	.cpload  t9
	   	.set  reorder

		subu	sp,sp,32

		sw		gp,16(sp)
		sw		$fp,20(sp)
		sw		ra,24(sp)

		move	$fp,sp

		sw		a0,32($fp)
		sw		a1,36($fp)
		sw		a2,40($fp)



		li		v0,SYS_read

		lw		a0,32($fp)
		lw		a1,36($fp)
		lw		a2,40($fp)

		syscall

		bne		a3,zero,read_error


		j		read_unwind

read_error:

	move v0,zero
	addi v1,zero,1

read_unwind:
		lw		gp,16(sp)
		lw		$fp,20(sp)
		lw		ra,24(sp)
		addu		sp,sp,32

		jr		ra

	.end		my_read

	/* ssize_t my_write(int,const void*,size_t) */
	.text
	.align 	2
	.globl	my_write
	.ent		my_write

my_write:
   	.frame $fp,32, ra
   	.set  noreorder
   	.cpload  t9
   	.set  reorder

		subu		sp,sp,32

		sw		gp,16(sp)
		sw		$fp,20(sp)
		sw		ra,24(sp)

		move		$fp,sp

		sw		a0,32($fp)
		sw		a1,36($fp)
		sw		a2,40($fp)

		li		v0,SYS_write

		lw		a0,32($fp)
		lw		a1,36($fp)
		lw		a2,40($fp)

		syscall

		bne		a3,zero,write_error

		beq a3,zero,write_unwind

write_error:
		move v0,zero
		addi v1,zero,1

write_unwind:
		lw		gp,16(sp)
		lw		$fp,20(sp)
		lw		ra,24(sp)
		addu		sp,sp,32

		jr		ra

	.end		my_write

	.ent	mystrlen
mystrlen:
		.frame	$fp, 16, ra
		subu	sp, sp, 16
		.cprestore 0
		sw		$fp, 4(sp)
		move	$fp, sp

		li		v0, 0 				## Use v0 for the result.
		beqz 	a0, mystrlen_return
mystrlen_loop:
		lb		t0, 0(a0)
		beqz	t0, mystrlen_return
		addiu	a0, a0, 1
		addiu	v0, v0, 1
		j		mystrlen_loop

mystrlen_return:

		lw		$fp, 4(sp) 			## Destruimos el frame.
		addu	sp, sp, 16


		j		ra					## Retorno.
		.end	mystrlen


		.ent	convertir
convertir:
		.frame	$fp, 16, ra
		subu 	sp, sp, 16
		sw 		gp, 8(sp)
		sw		$fp, 12(sp)
		move	$fp, sp

		sw 		a0, 16($fp) ## busco c
		sw 		a1, 20($fp) ## busco i

		sw 		t0, 0($fp) ##declaro mult

		beq 	a1,2,igual_64
		beq 	a1,1,igual_8

		li 		t0, 1
		sw 		t0, 0($fp)

comparacion:
		blt 	a0,48,return_menos1
		bge 	a0, 56, return_menos1

		subu 	a0,a0,48
		mult 	t0, a0
		sw 		t0, 0($fp)
		lw 		v0, 0($fp)
		j 		fin_convert
igual_64:
		li 		t0, 64
		sw		t0, 0($fp)
		j 		comparacion
igual_8:
		li 		t0, 8
		sw		t0, 0($fp)
		j 		comparacion

return_menos1:
		li 		v0,-1
		j 		fin_convert

fin_convert:
		lw		gp, 8(sp)
		lw		$fp, 12(sp)
		addu	sp, sp, 16

		j	ra 				## Retorno.

		.end	convertir

		.rdata
			.extern ejecucion_correcta
ejecucion_correcta:
			.asciiz	"Ejecución correcta"
			.extern	error_lectura
error_lectura:
			.asciiz	"ERROR en lectura"
			.extern	error_escritura
error_escritura:
			.asciiz	"ERROR en escritura"
			.extern	error_delimitador
error_delimitador:
			.asciiz	"ERROR delimitador incorrecto"
			.extern error_argumentos_incorrectos
error_argumentos_incorrectos:
			.asciiz	"ERROR argumentos incorrectos"
			.extern	odump_errmsg
			.align 2
odump_errmsg:
	.word	ejecucion_correcta, error_lectura, error_delimitador, error_argumentos_incorrectos
